﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics.Contracts;

namespace Konst3d.Efficiency.IO
{
    public static class BinaryReaderExtender
    {
        public static byte ReadByte(this BinaryReader reader, long offset, SeekOrigin origin = SeekOrigin.Begin)
        {
            if (offset >= 0)
            {
                reader.BaseStream.Seek(offset, origin);
            }

            return (byte)reader.ReadByte();
        }

        public static int ReadInt32(this BinaryReader reader, long offset, SeekOrigin origin = SeekOrigin.Begin)
        {
            if (offset >= 0)
            {
                reader.BaseStream.Seek(offset, origin);
            }

            return reader.ReadInt32();
        }

        public static uint ReadUInt32(this BinaryReader reader, long offset, SeekOrigin origin = SeekOrigin.Begin)
        {
            if (offset >= 0)
            {
                reader.BaseStream.Seek(offset, origin);
            }

            return reader.ReadUInt32();
        }

        public static double ReadSingle(this BinaryReader reader, long offset, SeekOrigin origin = SeekOrigin.Begin)
        {
            if (offset >= 0)
            {
                reader.BaseStream.Seek(offset, origin);
            }

            return reader.ReadSingle();
        }

        public static double ReadDouble(this BinaryReader reader, long offset, SeekOrigin origin = SeekOrigin.Begin)
        {
            if (offset >= 0)
            {
                reader.BaseStream.Seek(offset, origin);
            }

            return reader.ReadDouble();
        }

        public static DateTime ReadDateTime(this BinaryReader reader, long offset = -1, SeekOrigin origin = SeekOrigin.Begin)
        {
            if (offset >= 0)
            {
                reader.BaseStream.Seek(offset, origin);
            }

            int year = reader.ReadInt32();
            byte month = reader.ReadByte();
            byte day = reader.ReadByte();
            byte hour = reader.ReadByte();
            byte minute = reader.ReadByte();
            byte second = reader.ReadByte();

            return new DateTime(year, month, day, hour, minute, second);
        }

        public static ShortDate ReadShortDate(this BinaryReader reader, long offset = -1, SeekOrigin origin = SeekOrigin.Begin)
        {
            ShortDate date = new ShortDate();

            date.Year = reader.ReadInt32(offset, origin);
            date.Month = reader.ReadByte();
            date.Day = reader.ReadByte();

            return date;
        }

        public static string ReadString(this BinaryReader reader, Encoding encoding, long offset = -1, SeekOrigin origin = SeekOrigin.Begin)
        {
            Contract.Requires(encoding != null);

            string result = String.Empty;

            if (offset >= 0)
            {
                reader.BaseStream.Seek(offset, origin);
            }

            int length = reader.ReadInt32();

            if (length > 0)
            {
                var data = reader.ReadBytes(length);
                result = encoding.GetString(data);
            }

            return result;
        }

        public static string ReadString(this BinaryReader reader, ICryptographicEngine cryptographicEngine, long offset = -1, SeekOrigin origin = SeekOrigin.Begin)
        {
            Contract.Requires(cryptographicEngine != null);

            string result = String.Empty;

            if (offset >= 0)
            {
                reader.BaseStream.Seek(offset, origin);
            }

            int length = reader.ReadInt32();

            if (length > 0)
            {
                var data = reader.ReadBytes(length);
                result = cryptographicEngine.Decode(data, 0, data.Length);
            }

            return result;
        }

        public static byte[] ReadBytes(this BinaryReader reader, int count, long offset = -1, SeekOrigin origin = SeekOrigin.Begin)
        {
            Contract.Requires(count >= 0);

            if (offset >= 0)
            {
                reader.BaseStream.Seek(offset, origin);
            }

            var buffer = new byte[count];

            if (count > 0)
            {
                var red = reader.Read(buffer, 0, count);

                if (red != count)
                {
                    throw new IOException(String.Format("Failed to read file, tried to read {0} bytes, actually red {1} bytes.", count, red));
                }
            }

            return buffer;
        }
    }
}
